/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO

Authors: Nitay Megides
		 Rachel Banino

Project: Exercise 3

Using:	See Inc. list
		
Description:
Loader is basically a thread that loads the picture. It loads it row by row,
notifying the Locator threads (by event) on every row it reads. After reading 
each row, Loader converts the row to grayscale, then "publishes" it.

oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

#include "Loader.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "General.h"
#include "Manager.h"
#include "Locator.h"
#include "hw_code/IspImageAnalysis.h"
#include "Debug.h"
#include "Comparer.h"


// Info for the loader thread
TLoaderInformation LoaderInfo;

// Loader thread parameters
DWORD LoaderThreadID = 0;
HANDLE LoaderThreadHandle = NULL;

// Forward declaration of the threadfunc.
DWORD WINAPI LoaderMain(void* ptr);

BOOL LoaderInitialize( Bmp24LineReader reader )
{
	// Initialize the line reader
	LoaderInfo.LineReader = reader;

	if (!IsInitialized(LoaderInfo.LineReader))
	{
		REPORT_ERROR("Loader: Line reader is invalid\n");
		LoaderInfo.LineReader = NULL;
		return FALSE;
	}

	LoaderThreadHandle = CreateThread(NULL, 0, LoaderMain, NULL, CREATE_SUSPENDED, &LoaderThreadID);
	if (LoaderThreadHandle == NULL)
	{
		REPORT_ERROR("Loader: Couldn't create thread\n");
		return FALSE;
	}
	return TRUE;
}

void LoaderFree()
{
	DWORD waitResult;
	if (LoaderThreadHandle != NULL)
	{
		waitResult = WaitForSingleObject(LoaderThreadHandle, THREAD_TERMINATE_TIMEOUT);

		// Wait to the thread to die. Not dying? Kill it
		if (waitResult != WAIT_OBJECT_0)
		{
			REPORT_DEBUG("Loader: Thread is frozen. Terminating...\n");
			if (!TerminateThread(LoaderThreadHandle, 0))
				REPORT_ERROR("Loader: Terminating thread failed! Thread is a zombie\n");
		}

		CloseHandle(LoaderThreadHandle);
	}
}

DWORD WINAPI LoaderMain(void* ptr)
{
	Byte_t* row;
	int i = 0;
	EReadRowRes readRowResult = FAILURE;

	WaitForRunPermission();

	// Initialize everything
	row = (Byte_t*)malloc(GetPitch(LoaderInfo.LineReader));

	GlobalPicture.lastReadLine = -1;

	// Read a row
	readRowResult = ReadRow(LoaderInfo.LineReader, row);
	while ((readRowResult == SUCCESS) && (!ProgramTerminated()))
	{
		// Convert row to grayscale (Effectively promotes 3 bytes each loop (RGB))
		for (i = 0; i < GlobalPicture.PictureWidth; i++)
		{
			GlobalPicture.Picture[GlobalPicture.lastReadLine + 1][i] = 
				ConvertPixelColorToGrayscale(row[(i*3) + 2], row[(i*3) + 1], row[(i*3) + 0]);
		}

		// Carefully get the mutex and update lastReadLine
		if (!(WaitForSingleObject(GlobalPicture.lastReadLineProtector, INFINITE) == WAIT_OBJECT_0))
		{
			REPORT_ERROR("Loader: WaitForSingleObject Failed!\n");
			readRowResult = FAILURE;
			break;
		}
		else
		{
			// Update the last line read and release the mutex
			GlobalPicture.lastReadLine++;

			if (GlobalPicture.lastReadLine % 50 == 0)
				REPORT_DEBUG("Loader Position: %d. Faces Count: %d\n", GlobalPicture.lastReadLine, FacesCount());

			if (!ReleaseMutex(GlobalPicture.lastReadLineProtector))
				REPORT_ERROR("Loader: Can't release mutex\n");
		}

		// Notify the rest of the threads
		NotifyLineRead();

		// Read the next row
		readRowResult = ReadRow(LoaderInfo.LineReader, row);
	}

	if (readRowResult == FAILURE)
	{
		REPORT_ERROR("Loader: Error while loading the picture! Terminating...\n");
		TerminateProgram();
	}

	// We're done. Release everything
	free(row);

	// Debug Code (Not to be used in the final program)
	// SavePicture();

	NotifyReadingFinished();

	ReleaseRunPermission();

	return 0;
}

BOOL LoaderStartLoading()
{
	// Start LoaderMain
	return (ResumeThread(LoaderThreadHandle) > -1);
}
